#ifndef __LOG_H__
#define __LOG_H__

#include <string>
#include <stdint.h>
#include <memory>
#include <list>
#include <sstream>
#include <fstream>
#include <vector>
#include <stdarg.h>
#include <map>
#include <iostream>

#include "util.h"
#include "singleton.h"
#include "lock.h"
#include "thread.h"



/**
 * @brief 使用流式方式将日志级别level的日志写入到logger
 * LogEventWrap是一个临时对象，获取到SS后，会释放，执行其析构函数，此时就会自动进行日志输出
 */

/*

{	johnsonli::LogEvent::ptr evnt( new johnsonli::LogEvent(logger, level, \
		                        __FILE__, __LINE__, 0, johnsonli::getThreadId(),\
		                johnsonli::getFiberId(), time(0), "thread_name")); \
		evnt->getSS() << message;\
		logger->log(level, evnt);}
*/
 
#define LOG_LEVEL(logger, level) \
    if(logger->getLevel() <= level) \
        johnsonli::LogEventWrap(johnsonli::LogEvent::ptr(new johnsonli::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, johnsonli::getThreadId(),\
                johnsonli::getFiberId(), time(0), johnsonli::Thread::GetName()))).getSS()
		

 /**
  * @brief 使用流式方式将日志级别debug的日志写入到logger
  */
#define LOG_DEBUG(logger) LOG_LEVEL(logger, johnsonli::LogLevel::DEBUG)

  /**
   * @brief 使用流式方式将日志级别info的日志写入到logger
   */
#define LOG_INFO(logger) LOG_LEVEL(logger, johnsonli::LogLevel::INFO)

   /**
	* @brief 使用流式方式将日志级别warn的日志写入到logger
	*/
#define LOG_WARN(logger) LOG_LEVEL(logger, johnsonli::LogLevel::WARN)

	/**
	 * @brief 使用流式方式将日志级别error的日志写入到logger
	 */
#define LOG_ERROR(logger) LOG_LEVEL(logger, johnsonli::LogLevel::ERROR)

	 /**
	  * @brief 使用流式方式将日志级别fatal的日志写入到logger
	  */
#define LOG_FATAL(logger) LOG_LEVEL(logger, johnsonli::LogLevel::FATAL)


	  /**
	   * @brief 使用格式化方式将日志级别level的日志写入到logger
	   */
#define LOG_FMT_LEVEL(logger, level, fmt, ...) \
    if(logger->getLevel() <= level) \
        johnsonli::LogEventWrap(johnsonli::LogEvent::ptr(new johnsonli::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, johnsonli::getThreadId(),\
                johnsonli::getFiberId(), time(0), johnsonli::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)

	   /**
		* @brief 使用格式化方式将日志级别debug的日志写入到logger
		*/
#define LOG_FMT_DEBUG(logger, fmt, ...) LOG_FMT_LEVEL(logger, johnsonli::LogLevel::DEBUG, fmt, __VA_ARGS__)

		/**
		 * @brief 使用格式化方式将日志级别info的日志写入到logger
		 */
#define LOG_FMT_INFO(logger, fmt, ...)  LOG_FMT_LEVEL(logger, johnsonli::LogLevel::INFO, fmt, __VA_ARGS__)

		 /**
		  * @brief 使用格式化方式将日志级别warn的日志写入到logger
		  */
#define LOG_FMT_WARN(logger, fmt, ...)  LOG_FMT_LEVEL(logger, johnsonli::LogLevel::WARN, fmt, __VA_ARGS__)

		  /**
		   * @brief 使用格式化方式将日志级别error的日志写入到logger
		   */
#define LOG_FMT_ERROR(logger, fmt, ...) LOG_FMT_LEVEL(logger, johnsonli::LogLevel::ERROR, fmt, __VA_ARGS__)

		   /**
			* @brief 使用格式化方式将日志级别fatal的日志写入到logger
			*/
#define LOG_FMT_FATAL(logger, fmt, ...) LOG_FMT_LEVEL(logger, johnsonli::LogLevel::FATAL, fmt, __VA_ARGS__)

#define LOG_ROOT() johnsonli::LoggerMgr::GetInstance()->getRoot()

#define LOG_NAME(name) johnsonli::LoggerMgr::GetInstance()->getLogger(name)

namespace johnsonli
{
	class Logger;

	/**
	* @brief 日志级别
	*/
	class LogLevel
	{
	public:
		enum Level
		{
			UNKNOW = 0,
			DEBUG = 1,
			INFO = 2,
			WARN = 3,
			ERROR = 4,
			FATAL = 5
		};

		/**
		 * @brief 将日志级别转成文本输出
		 * @param[in] level 日志级别
		 */
		static const char* ToString(LogLevel::Level level);

		/**
		 * @brief 将文本转换成日志级别
		 * @param[in] str 日志级别文本
		 */
		static LogLevel::Level FromString(const std::string& str);
	};

	/**
	* @brief 日志事件
	*/
	class LogEvent {
	public:
		typedef std::shared_ptr<LogEvent> ptr;
		/**
		 * @brief 构造函数
		 * @param[in] logger 日志器
		 * @param[in] level 日志级别
		 * @param[in] file 文件名
		 * @param[in] line 文件行号
		 * @param[in] elapse 程序启动依赖的耗时(毫秒)
		 * @param[in] thread_id 线程id
		 * @param[in] fiber_id 协程id
		 * @param[in] time 日志事件(秒)
		 * @param[in] thread_name 线程名称
		 */
		LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
			, const char* file, int32_t line, uint32_t elapse
			, uint32_t thread_id, uint32_t fiber_id, uint64_t time
			, const std::string& thread_name);

		//~LogEvent();

		/**
		 * @brief 返回文件名
		 */
		const char* getFile() const { return m_file; }

		/**
		 * @brief 返回行号
		 */
		int32_t getLine() const { return m_line; }

		/**
		 * @brief 返回耗时
		 */
		uint32_t getElapse() const { return m_elapse; }

		/**
		 * @brief 返回线程ID
		 */
		uint32_t getThreadId() const { return m_threadId; }

		/**
		 * @brief 返回协程ID
		 */
		uint32_t getFiberId() const { return m_fiberId; }

		/**
		 * @brief 返回时间
		 */
		uint64_t getTime() const { return m_time; }

		/**
		 * @brief 返回线程名称
		 */
		const std::string& getThreadName() const { return m_threadName; }

		/**
		 * @brief 返回日志内容
		 */
		std::string getContent() const { return m_ss.str(); }

		/**
		 * @brief 返回日志器
		 */
		std::shared_ptr<Logger> getLogger() const { return m_logger; }

		/**
		 * @brief 返回日志级别
		 */
		LogLevel::Level getLevel() const { return m_level; }

		/**
		 * @brief 返回日志内容字符串流
		 */
		std::stringstream& getSS() { return m_ss; }

		/**
		 * @brief 格式化写入日志内容
		 */
		void format(const char* fmt, ...);

		/**
		 * @brief 格式化写入日志内容
		 */
		void format(const char* fmt, va_list al);
	private:
		/// 文件名
		const char* m_file = nullptr;
		/// 行号
		int32_t m_line = 0;
		/// 程序启动开始到现在的毫秒数
		uint32_t m_elapse = 0;
		/// 线程ID
		uint32_t m_threadId = 0;
		/// 协程ID
		uint32_t m_fiberId = 0;
		/// 时间戳
		uint64_t m_time = 0;
		/// 线程名称
		std::string m_threadName;
		/// 日志内容流
		std::stringstream m_ss;
		/// 日志器
		std::shared_ptr<Logger> m_logger;
		/// 日志等级
		LogLevel::Level m_level;
	};

	class LogEventWrap
	{
	public:
		LogEventWrap(LogEvent::ptr event);
		~LogEventWrap();

		LogEvent::ptr getEvent() const { return m_event; }
		std::stringstream& getSS();
	private:
		LogEvent::ptr m_event;
	};
	
	/**
	 * @brief 日志格式化
	 */
	class LogFormatter {
	public:
		typedef std::shared_ptr<LogFormatter> ptr;
		/**
		 * @brief 构造函数
		 * @param[in] pattern 格式模板
		 * @details 
		 *  %m 消息
		 *  %p 日志级别
		 *  %r 累计毫秒数
		 *  %c 日志名称
		 *  %t 线程id
		 *  %n 换行
		 *  %d 时间
		 *  %f 文件名
		 *  %l 行号
		 *  %T 制表符
		 *  %F 协程id
		 *  %N 线程名称
		 *
		 *  默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
		 */
		LogFormatter(const std::string& pattern);

		/**
		 * @brief 返回格式化日志文本
		 * @param[in] logger 日志器
		 * @param[in] level 日志级别
		 * @param[in] event 日志事件
		 */
		std::string format(LogLevel::Level level, LogEvent::ptr event);
		std::ostream& format(std::ostream& ofs, LogLevel::Level level, LogEvent::ptr event);
	public:

		/**
		 * @brief 日志内容项格式化
		 */
		class FormatItem {
		public:
			typedef std::shared_ptr<FormatItem> ptr;
			/**
			 * @brief 析构函数
			 */
			virtual ~FormatItem() {}
			/**
			 * @brief 格式化日志到流
			 * @param[in, out] os 日志输出流
			 * @param[in] level 日志等级
			 * @param[in] event 日志事件
			 */
			virtual void format(std::ostream& os, LogLevel::Level level, LogEvent::ptr event) = 0;
		};

		/**
		 * @brief 初始化,解析日志模板
		 */
		void init();

		/**
		 * @brief 是否有错误
		 */
		bool isError() const { return m_error;}

		/**
		 * @brief 返回日志模板
		 */
		const std::string getPattern() const { return m_pattern;}
	private:
		/// 日志格式模板
		std::string m_pattern;
		/// 日志格式解析后格式
		std::vector<FormatItem::ptr> m_items;
		/// 是否有错误
		bool m_error = false;

	};

	/**
	 * @brief 日志输出地
	 */
	class LogAppender
	{
	public:
		typedef SpinLock MutexType;
		typedef std::shared_ptr<LogAppender> ptr;
		LogAppender();
		LogAppender(LogLevel::Level level, LogFormatter::ptr formater);
		virtual ~LogAppender();

		virtual void log(LogLevel::Level level, LogEvent::ptr event) = 0;

		void setFormatter(LogFormatter::ptr val);
		LogFormatter::ptr getFormatter();

		LogLevel::Level getLevel();
		void setLevel(LogLevel::Level val);

		virtual std::string toYamlString() = 0;

	protected:
		LogLevel::Level m_level = LogLevel::DEBUG;
		LogFormatter::ptr m_formater;
		MutexType m_mutex;
	};

	/**
	 * @brief 日志器
	 */
	class Logger
	{
	public:
		typedef SpinLock MutexType;
		Logger(LogLevel::Level level, const std::string& name = "root");
		Logger(const std::string& name = "root");
	public:
		typedef std::shared_ptr<Logger> ptr;
		//static Logger::ptr getInstance(LogLevel::Level level, const std::string& name = "root");

		void log(LogLevel::Level level, LogEvent::ptr);

		void debug(LogEvent::ptr event);
		void info(LogEvent::ptr event);
		void warn(LogEvent::ptr event);
		void error(LogEvent::ptr event);
		void fatal(LogEvent::ptr event);

		void addAppender(LogAppender::ptr appender);
		void delAppender(LogAppender::ptr appender);

		LogLevel::Level getLevel();
		void setLevel(LogLevel::Level val);

		std::string getName();
		void setName(std::string name);

		void clearAppenders();

		std::string toYamlString();

	private:
		std::string m_name;							//日志名称
		LogLevel::Level m_level;					//日志级别
		std::vector<LogAppender::ptr> m_appenders;	//Appender集合
		MutexType m_mutex;
		
	};

	/**
	 * @brief 输出到控制台的
	 */
	class StdoutLogAppender : public LogAppender
	{
	public:
		typedef std::shared_ptr<StdoutLogAppender> ptr;
		StdoutLogAppender();
		StdoutLogAppender(LogLevel::Level level, LogFormatter::ptr formater);
		void log(LogLevel::Level level, LogEvent::ptr event) override;
		std::string toYamlString() override;
	private:
	};

	/**
	 * @brief 输出到文件的Appender
	 */
	class FileLogAppender : public LogAppender
	{
	public:
		typedef std::shared_ptr<FileLogAppender> ptr;
		FileLogAppender(const std::string& filename);
		FileLogAppender(const std::string& filename, LogLevel::Level level, LogFormatter::ptr formater);
		~FileLogAppender();

		//重新打开文件，文件打开成功，返回true
		bool reopen();
		void log(LogLevel::Level level, LogEvent::ptr event) override;
		std::string toYamlString() override;
	private:
		std::string m_filename;
		std::ofstream m_filestream;
		/// 上次重新打开时间
		uint64_t m_lastTime = 0;
	};

	class LoggerManager
	{
	public:
		typedef SpinLock MutexType;
		//static Logger::ptr getLogger(LogLevel::Level level, const std::string& name);
		Logger::ptr getLogger(const std::string & name);
		Logger::ptr getRoot() 
		{ 
			//std::cout << "getRoot" << std::endl;
			return m_root; 
		}
		LoggerManager();

		std::string toYamlString();
	private:
		std::map<std::string, Logger::ptr> m_loggers;
		Logger::ptr m_root;
		MutexType m_mutex;
	};

	typedef johnsonli::Singleton<LoggerManager> LoggerMgr;
}



#endif 


